// Posix-compliance functions.

#include <errno.h>
#include <Process.h>
#include <FileDescriptor.h>
#include <string.h>
#include <errno.h>
#include <File.h>
#include <resource.h>
#include <ResourceManager.h>
#include "../../initrd/tty/Tty_IF.h"
#include <pwd.h>
#include <sys/time.h>
#include <Timer.h>

extern s32int syscall(u32int id);
extern s32int syscall1(u32int id, s32int val);

#undef errno
extern int errno;

extern u32int _process;
extern Address _currentKernelStack;

extern "C" int waitpid(int pid, int *stat_loc, int options)
{
  int a = 0;
  while (a == 0)
  {
    a = callResource(_process, Process_IF::waitPid, pid, stat_loc, options);
    if (options & 2) // WNOHANG
    {
      break;
    }
  }

  return a;
}

extern "C" int wait(int *stat_loc)
{
  return waitpid(-1, stat_loc, 0);
}

extern "C" int tcgetattr(int filedes, struct termios *termios_p)
{
  u32int resourceManager = requestResource("ResourceManager");
  u32int fd = callResource(_process, Process_IF::getFileDescriptor, filedes);
  if (!fd)
  {
    errno = ENOTTY;
    return -1;
  }
  
  u32int ttyResource = callResource(fd, File_IF::getResource);
  if (!ttyResource)
  {
    errno = ENOTTY;
    return -1;
  }
  
  bool wasUserHeap = true;
  termios_t *tptr = termios_p;
  if (!USER_HEAP_ADDRESS(termios_p))
  {
    tptr = (termios_t*)callResource(resourceManager, ResourceManager_IF::share, NULL, 0);
    wasUserHeap = false;
  }
  callResource(ttyResource, Tty_IF::tcgetattr, tptr);
  if (!wasUserHeap)
  {
    callResource(resourceManager, ResourceManager_IF::unshare, tptr, termios_p, sizeof(termios_t));
  }
  return 0;
}

extern "C" int tcsetattr(int filedes, int optional_actions, struct termios *termios_p)
{
  u32int resourceManager = requestResource("ResourceManager");
  // We currently totally ignore optimal_actions and do everything immediately.
  // screw POSIX! :)
  u32int fd = callResource(_process, Process_IF::getFileDescriptor, filedes);
  if (!fd)
  {
    errno = ENOTTY;
    return -1;
  }
  
  u32int ttyResource = callResource(fd, File_IF::getResource);
  if (!ttyResource)
  {
    errno = ENOTTY;
    return -1;
  }
  
  bool wasUserHeap = true;
  termios_t *tptr = termios_p;
  if (!USER_HEAP_ADDRESS(termios_p))
  {
    tptr = (termios_t*)callResource(resourceManager, ResourceManager_IF::share, tptr, sizeof(termios_t));
    wasUserHeap = false;
  }
  callResource(ttyResource, Tty_IF::tcsetattr, tptr);
  if (!wasUserHeap)
  {
    callResource(resourceManager, ResourceManager_IF::unshare, tptr, NULL, 0);
  }
  return 0;
}

extern "C" int _gettimeofday(struct timeval *tv, void *restrict)
{
  u32int timer = requestResource("Timer");
  u32int tick = callResource(timer, Timer_IF::getTick);
  tv->tv_sec = tick / 1000;
  tv->tv_usec = (tick % 1000); // get milliseconds.
  tv->tv_usec *= 1000; // Convert to microseconds.
  return 0;
}

extern "C" int gettimeofday(struct timeval *tv, struct timezone *restrict)
{
  u32int timer = requestResource("Timer");
  u32int tick = callResource(timer, Timer_IF::getTick);
  tv->tv_sec = tick / 1000;
  tv->tv_usec = (tick % 1000); // get milliseconds.
  tv->tv_usec *= 1000; // Convert to microseconds.
  return 0;
}

extern "C" long sysconf(int name)
{
  errno = EINVAL;
  return -1;
}

extern "C" int getuid()
{
  return 0;
}

extern "C" int getgid()
{
  return 0;
}

extern "C" int geteuid()
{
  return 0;
}

extern "C" int getegid()
{
  return 0;
}

extern "C" int gethostname(char *name, size_t namelen)
{
  strcpy(name, "test");
  return 0;
}

extern "C" char *getcwd(char *buf, size_t size)
{
  if(size == 0)
  {
    char *cwd = new char[2];
    cwd[0] = '/';
    cwd[1] = '\0';
    return cwd;
  }
  strcpy(buf, "/");
  return buf;
}

extern "C" int getppid()
{
  return 0;
}

extern "C" struct passwd *getpwuid(uid_t uid)
{
  struct passwd *p = new struct passwd;
  p->pw_name = new char[6];
  strcpy(p->pw_name, "james");
  p->pw_name[5] = 0;
  p->pw_uid = 0;
  p->pw_gid = 0;
  p->pw_dir = new char[2];
  strcpy(p->pw_dir, "/");
  p->pw_dir[1] = 0;
  p->pw_shell = 0;
  return p;
}

extern "C" void endpwent()
{
}

extern "C" void setpwent()
{
}

extern "C" int ioctl(int filedes, long unsigned int request, ...)
{
  errno = ENODEV;
  return -1;
}

// extern "C" int tgetent(char *c, const char *d)
// {
//   return -1;
// }

extern "C" int yield()
{
  return syscall(SYSCALL_YIELD);
}

extern "C" const char * const sys_siglist[] = {
  "Hangup",
  "Interrupt",
  "Quit",
  "Illegal instruction",
  "Trap",
  "IOT",
  "Abort",
  "EMT",
  "Floating point exception",
  "Kill",
  "Bus error",
  "Segmentation violation",
  "Bad argument to system call",
  "Pipe error",
  "Alarm",
  "Terminate" };

extern "C" char *strsignal(int sig)
{
  if (sig < 16)
    return (char*)sys_siglist[sig];
  else
  return (char*)"Unknown";
}

extern "C" int setuid(int uid)
{
  errno = EINVAL;
  return -1;
}

extern "C" int setgid(int gid)
{
  errno = EINVAL;
  return -1;
}

extern "C" unsigned int sleep(unsigned int seconds)
{
  return 0;
}

extern "C" unsigned int alarm(unsigned int seconds)
{
  return 0;
}

extern "C" char *ttyname(int filedes)
{
  return (char*)"wibble!";
}

extern "C" int umask(int mask)
{
  return 0;
}

extern "C" int access(const char *path, int amode)
{
  errno = ENOENT;
  return -1;
}

extern "C" const char * const sys_errlist[] = {};
extern "C" const int sys_nerr = 0;
long timezone;

extern "C" int tcflow(int flow, int action)
{
  errno = ENOTTY;
  return -1;
}

extern "C" long pathconf(const char *path, int name)
{
  return 0;
}

extern "C" int select(int nfds, struct fd_set * readfds,
                      struct fd_set * writefds, struct fd_set * errorfds,
                      struct timeval * timeout)
{
  errno = ENOSYS;
  return -1;
}

extern "C" void setgrent()
{
}

extern "C" void endgrent()
{
}

extern "C" struct group *getgrent()
{
  errno = ENOSYS;
  return NULL;
}

extern "C" int chdir(const char *path)
{
  return 0;
}

extern "C" int dup(int fileno)
{
  errno = ENOSYS;
  return -1;
}

extern "C" int pipe(int filedes[2])
{
  errno = ENOSYS;
  return -1;
}

extern "C" int fcntl(int fildes, int cmd, ...)
{
  errno = ENOSYS;
  return -1;
}

